package net.sansi.v3correctserver.config.matrixloader;

import lombok.extern.slf4j.Slf4j;
import net.sansi.v3correctserver.exception.ServiceException;
import net.sansi.v3correctserver.util.Matrix;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.file.Path;

/**
 * Created by Fang
 * create time : 2024/11/8
 * IDE : IntelliJ IDEA
 * <p>
 * Set 文件加载器基类
 * 默认实现基础写入逻辑， 子类必须实现加载和保存具体内容逻辑
 */
@Slf4j
public abstract class MatrixFileLoaderBase<T> implements Matrix.FileLoader<T> {
    // 元素长度
    protected int itemLen;

    public MatrixFileLoaderBase(int itemLen) {
        this.itemLen = itemLen;
    }

    /**
     * 加载逻辑
     * 使用BUFFER优化，避免频繁的读取磁盘
     *
     * @param path 文件路径
     * @return 矩阵对象
     */
    @Override
    public Matrix<T> load(Path path) {
        try (DataInputStream dis = new DataInputStream(new FileInputStream(path.toFile()))) {
            ByteBuffer buffer = ByteBuffer.wrap(dis.readAllBytes());
            int w = buffer.getInt();
            int h = buffer.getInt();
            itemLen = buffer.getInt();
            Matrix<T> matrix = new Matrix<>(w, h);
            for (int x = 0; x < w; x++) {
                for (int y = 0; y < h; y++) {
                    int len = itemLen < 1 ? buffer.getInt() : itemLen;
                    matrix.set(x, y, readItemBuffer(buffer, len));
                }
            }
            return matrix;
        } catch (Exception e) {
            throw new ServiceException("读取矩阵失败 Double !", e);
        }
    }

    /**
     * 保存逻辑
     * 这里使用 BUFFER 优化，避免频繁的写入磁盘
     *
     * @param matrix 矩阵对象
     * @param path   文件路径
     */
    @Override
    public void save(Matrix<T> matrix, Path path) {
        try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(path.toFile()))) {
            int w = matrix.getWidth(), h = matrix.getHeight();
            dos.writeInt(w);
            dos.writeInt(h);
            dos.writeInt(itemLen);
            ByteBuffer buffer = ByteBuffer.allocate(5 * 1024 * 1024);
            for (int x = 0; x < w; x++) {
                for (int y = 0; y < h; y++) {
                    T item = matrix.get(x, y);
                    int len = getItemBufferSize(item);
                    if (buffer.position() + len >= buffer.capacity()) {
                        dos.write(buffer.array(), 0, buffer.position());
                        buffer.clear();
                    }
                    if (itemLen < 1) buffer.putInt(getItemSize(item));
                    saveItemBuffer(buffer, item);
                }
            }
            if (buffer.position() != 0) {
                dos.write(buffer.array(), 0, buffer.position());
            }
        } catch (Exception e) {
            throw new ServiceException("矩阵保存失败! {}", path.toUri().toString(), e);
        }
    }

    /**
     * 获取一个元素所占的长度，是指数组长度
     * 举例： 元素为 int[3], 长度就是 3;
     *
     * @param item 元素
     * @return 长度 [item size, buffer size]
     */
    abstract int getItemSize(T item);

    /**
     * 获取一个元素所占的缓冲区大小
     * 举例： 元素为 int[3], buffer 大小就是 3 * Integer.BYTES;
     *
     * @param item 元素
     * @return buffer 大小
     */
    abstract int getItemBufferSize(T item);

    /**
     * 获取一个元素所占的缓冲区大小
     * 这里需要提供元素的长度， 子类可以根据长度计算缓冲区大小
     *
     * @param len 元素长度 int[3]， 长度为 3
     * @return 缓冲区大小
     */
    abstract int getItemBufferSize(int len);

    /**
     * 保存一个元素
     *
     * @param buffer 数据输出流
     * @param item   元素
     */
    abstract void saveItemBuffer(ByteBuffer buffer, T item) throws Exception;

    /**
     * 读取一个元素
     *
     * @param buffer buffer
     * @param size   长度
     * @return 元素
     */
    abstract T readItemBuffer(ByteBuffer buffer, int size) throws Exception;

}
